﻿using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;

public class ZoomLevel
{
public float Height = 1.0f;
public float PortraitHeight = 1.0f;
public float FieldOfView = 60.0f;
}
public class CameraGesture : MonoBehaviour ,IInput
{
    public struct MapBound
    {
        public float top;
        public float left;
        public float bottom;
        public float right;
    }
    public interface IGestureHandler
    {
        void OnEnter();
        void OnExit();
        bool IsCameraOn();
        void OnPointDown(Vector2 screenPos, Vector3 worldPos);
        void OnPointUp(Vector2 screenPos, Vector3 worldPos);
        void OnTap(Vector2 screenPos, Vector3 worldPos);
        void OnZoom(int zoomLevel);
        bool OnDrag(Vector2 screenPos);
    }

    public static Vector3 nullPointer = new Vector3(-99999, -99999, -99999);
    // *********************************
    public ZoomLevel[] ZoomLevels;
    public float maxHeight = 20;
    public float minHeight = 8;
    public Camera mCamera = null;
    //
    private Transform cameraTransform;
    private Ray ray;
    private RaycastHit rayHit;
    private Vector3 pointerDownPos = nullPointer;
    private float perLevelHeight = 0;
    private float zoomFactor = 10;
    private float lastPointerTime = 0;
    private float factor = 1;
    private int _curZoom = 2;
    private int lastZoom = 2;
    private bool isControlled = false;
    private bool isZooming = false;

    static CameraGesture _Instance;
    private IGestureHandler mHandler;
    private IInputProvider mInputProvider;
    public static CameraGesture Instance
    {
        get {
            return _Instance;
        }
    }
    public int CurrentZoom
    {
        get {
            return _curZoom;
        }
        set { _curZoom = value; }
    }
    public ZoomLevel CurZoomLevel
    {
        get
        {
            return ZoomLevels[_curZoom];
        }
    }
    void Awake()
    {
        if(_Instance)
        {
            _Instance.DOKill();
            if (_Instance.gameObject != gameObject)
            {
                throw new System.Exception("你怎么能构造两个这玩意");
            }
            else
                GameObject.Destroy(_Instance);
        }
        _Instance = this;
#if UNITY_EDITOR || UNITY_STANDALONE
        mInputProvider = new MouseProvider(this);
#else
        mInputProvider = new TouchProvider(this);
#endif
        if (!mCamera)
            mCamera = gameObject.GetComponent<Camera>();
        cameraTransform = mCamera.transform;
        SetZoomLevels();
        cameraTransform.localPosition = (-cameraTransform.forward.normalized) * maxHeight;
    }
    public void Enter(IGestureHandler handler)
    {
        mHandler = handler;
        mCamera.enabled = true;
        isZooming = false;
        UpdateShadowDistance();
        UpdateMoveSpeedFactor();
        handler.OnEnter();
    }
    public void Exit()
    {
        mHandler.OnExit();
        mHandler = null;
        mCamera.enabled = false;
        isZooming = false;
    } 

    public void UpdateShadowDistance()
    {
        float diffValue = 0;
        float shadowDistance = 0;
        shadowDistance = perLevelHeight * _curZoom * 2 + 10;
        QualitySettings.shadowDistance = shadowDistance + diffValue;
    }
    
    private IEnumerator DelayReset(float delay = 0.1f)
    {
        yield return new WaitForSeconds(delay);
        Input.ResetInputAxes();
    }

    public void ZoomCameraViewToManual(ZoomLevel zl, float duration, System.Action<object> complete = null, object param = null)
    {
        isControlled = true;
        ZoomCameraViewTo(zl,duration,complete,param);
    }
    private void ZoomCameraViewTo(ZoomLevel zl, float duration, System.Action<object> complete = null, object param = null)
    {
        DOTween.Kill(cameraTransform);
        StopCoroutine("MoveCameraCoroutine");
        Vector3 delta = (-cameraTransform.forward.normalized) * zl.Height;
        Vector3 curr = (cameraTransform.localPosition.y / delta.y ) * delta;
        Vector3 to = cameraTransform.localPosition + (delta - curr);
                
        StartCoroutine("MoveCameraCoroutine", new Dictionary<string, object>()
        {
            { "pos", to },
            { "duration", duration }, 
            { "complete", complete }, 
            { "param", param }
        });
        isZoomEffecting = true;
    }
    bool isZoomEffecting = false;
    private IEnumerator MoveCameraCoroutine(Dictionary<string, object> argvDic)
    {
        Vector3 pos = (Vector3)argvDic["pos"];
        float duration = (float)argvDic["duration"];
        System.Action<object> complete = argvDic["complete"] as System.Action<object>;
        object param = argvDic["param"];
        yield return new WaitForEndOfFrame();
        cameraTransform.DOLocalMove(pos, duration).SetEase(Ease.Linear).OnComplete(() =>
        {
            isZoomEffecting = false;
            isControlled = false;
            if (null != complete) complete(param);
        });
    }

    /// <summary>
    /// 地图边界 （minx,maxx,minz,maxz）
    /// </summary>
    private MapBound mapBounds = new MapBound();
    private void CheckBounds(Vector2 delta)
    {
        Rect screen = new Rect(0,0,1,1);
        Ray r = mCamera.ViewportPointToRay(new Vector3(0.5f, 0.5f));
        Vector3 center = Vector3.zero;
        if (Physics.Raycast(ray, out rayHit))
        {
            center = rayHit.point;
        }

        //上下
        if (delta.y > 0)
        {
            float top = mapBounds.top + paddingEdge.y * 2;
            Vector2 sc = mCamera.WorldToViewportPoint(new Vector3(center.x,0,top));
            if (!screen.Contains(sc))
            {
                cameraTransform.position += new Vector3(0,0,delta.y);
            }
        }
        else if (delta.y < 0)
        {
            float bottom = mapBounds.bottom - paddingEdge.y;
            Vector2 sc = mCamera.WorldToViewportPoint(new Vector3(center.x,0,bottom));
            if (!screen.Contains(sc))
            {
                cameraTransform.position += new Vector3(0,0,delta.y);
            }
        } 
        //左右
        if (delta.x > 0)
        {
            float right = mapBounds.right + paddingEdge.x;
            Vector2 sc = mCamera.WorldToViewportPoint(new Vector3(right,0,center.z));
            if (!screen.Contains(sc))
            {
                cameraTransform.position += new Vector3(delta.x,0,0);
            }
        }
        else if (delta.x < 0)
        {
            float left = mapBounds.left - paddingEdge.x;
            Vector2 sc = mCamera.WorldToViewportPoint(new Vector3(left,0,center.z));
            if (!screen.Contains(sc))
            {
                cameraTransform.position += new Vector3(delta.x,0,0);
            }
        }
    }
    public void SetZoomLevels(){
        perLevelHeight = (maxHeight - minHeight) / 7.0f;
        ZoomLevels = new ZoomLevel[7];
        for (int i = 0; i < 7; i++)
        {
            ZoomLevel zl = new ZoomLevel();
            zl.Height = minHeight + i * perLevelHeight;
            ZoomLevels[i] = zl;
        }
    }
    public void SetZoomLevel()
    {
        UpdateShadowDistance();
        UpdateMoveSpeedFactor();
    }
    private void UpdateMoveSpeedFactor()
    {//更新移动屏幕比例speed
        Vector3 center = mCamera.ViewportToScreenPoint(new Vector3(0.5f, 0.5f));
        Ray ray = mCamera.ScreenPointToRay(center);
        if (Physics.Raycast(ray, out rayHit))
        {
            center = rayHit.point;
            Vector3 v1 = mCamera.WorldToScreenPoint(center + Vector3.left);
            Vector3 v2 = mCamera.WorldToScreenPoint(center + Vector3.right);
            factor = Vector3.Distance(v1, v2);// * Time.deltaTime;
            factor = 2 / factor; // * 5;
            
            BoxCollider mapCollider = rayHit.collider as BoxCollider;
            mapBounds.left =mapCollider.transform.position.x - mapCollider.bounds.extents.x;
            mapBounds.right = mapCollider.transform.position.x + mapCollider.bounds.extents.x;
            mapBounds.top = mapCollider.transform.position.z + mapCollider.bounds.extents.z;
            mapBounds.bottom =mapCollider.transform.position.z -  mapCollider.bounds.extents.z;
        }
    } 
    public void OnPointerDown(Vector2 pos)
    {
        ray = mCamera.ScreenPointToRay(pos);
        if (Physics.Raycast(ray, out rayHit))
        {
            Vector3 hitPos = rayHit.point;
            mHandler.OnPointDown(pos, hitPos);
        }
        else
            mHandler.OnPointDown(pos, nullPointer);
        pointerDownPos = pos;
        lastPointerTime = Time.time;
    }
    public void OnPointerUp(Vector2 pos)
    {
        ray = mCamera.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out rayHit))
        {
            Vector3 hitPos = rayHit.point;
            mHandler.OnPointUp(pos, hitPos);
            if (Time.time - lastPointerTime < 0.35f && Vector3.Distance(pos, pointerDownPos) < 5)
            {
                mHandler.OnTap(pos, hitPos);
            }
        }        
       // StartCoroutine(DelayReset(0.2f));
    }


    public void OnPointerMove(Vector2 pixelPosition, Vector2 delta)
    {
        if (!isZooming && isCameraOn)
        {//拖拽
            if (delta.magnitude > 1)
            {
                Vector2 pp = mInputProvider.PointerPosition;
                if (!mHandler.OnDrag(pp))
                {
                    delta.y = -delta.y * factor;// * 0.2f;
                    delta.x = -delta.x * factor;// * 0.2f;
                    CheckBounds(delta);
                }
            }
        }
    }

    public void OnPointerZoom(Vector2 pixelPosition, float dis)
    {
        if (dis != 0 && !isZoomEffecting)
        {
            if (dis > 0 && dis > zoomFactor)// && v.y < 25.0f)
            {//缩小
                if (_curZoom < ZoomLevels.Length - 1)
                {
                    _curZoom++;
                    SetZoomLevel();
                    mHandler.OnZoom(_curZoom);
                }
            }
            else if (dis < 0 && dis < -zoomFactor )// && v.y > 3.0f)
            {//放大
                if (_curZoom > 0)
                {
                    _curZoom--;
                    SetZoomLevel();
                    mHandler.OnZoom(_curZoom);
                }
            }
            isZooming = true;
        }
    }
    bool isCameraOn = true;
    public void OnUpdate()
    {
        if (isControlled || mHandler == null || !mHandler.IsCameraOn())
        {
            if(isCameraOn)
                isCameraOn = false;
            return;
        }
        mInputProvider.Update();
        if (isZooming)
        {
            if (lastZoom != _curZoom)
            {
                lastZoom = _curZoom;
                ZoomCameraViewTo(CurZoomLevel, 0.1f);
            }
            if (!isZoomEffecting)
            {
                isZooming = false;
            }
        }
        if (!isCameraOn)
            isCameraOn = true;
    }

    /// ***************** 编辑用 ***********
    [SerializeField]
    private Vector2 paddingEdge = new Vector2(1,1f);
    // [SerializeField]
    // private bool isDebugSelfUpdate = false;
    [ContextMenu("ResetZoomLevels")]
    void ResetZoomLevels()
    {
        perLevelHeight = (maxHeight - minHeight) / 7.0f;
        ZoomLevels = new ZoomLevel[7];
        for (int i = 0; i < 7; i++)
        {
            ZoomLevel zl = new ZoomLevel();
            zl.Height = minHeight + i * perLevelHeight;
            ZoomLevels[i] = zl;
        }
        SetZoomLevel();
        mHandler.OnZoom(_curZoom);
        isZooming = true;
    }
}
